/*
 * StartApplication.java
 *
 * Created on Jul 21, 2009 4:48:43 PM;
 */

package org.sunspotworld;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.peripheral.radio.RadioFactory;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.peripheral.TimeoutException;
import com.sun.spot.resources.transducers.ISwitch;
import com.sun.spot.resources.transducers.ISwitchListener;
import com.sun.spot.resources.transducers.ITriColorLED;
import com.sun.spot.resources.transducers.LEDColor;
import com.sun.spot.sensorboard.peripheral.ILightSensor;
import com.sun.spot.util.*;

import java.io.IOException;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

/**
 * The startApp method of this class is called by the VM to start the
 * application.
 * 
 * The manifest specifies this class as MIDlet-1, which means it will
 * be selected for execution.
 */
public class StartApplication extends MIDlet implements ISwitchListener {

    private ITriColorLED [] leds = EDemoBoard.getInstance().getLEDs();
    private ILightSensor light = EDemoBoard.getInstance().getLightSensor();
    private static final long SECONDS = 1000;
    private static final long PAUSE = 4*SECONDS;

    private static final long[] BLINK = {450, 1*SECONDS};  //on time, blink every (ex.  keep the lights on 450ms out of every 1s)
    private static final int N_BLINK_TIMES = 5;

    private BlinkState myState = new BlinkState(BlinkState.ONLINE_NOTBLINK, (byte)0, (byte)0, (byte)0);

    private static final String DATA_PORT = "87";
    private static final String CMD_PORT = "89";
    private static final int CHANNEL = 24;

    private static final LEDColor DEFAULT = LEDColor.RED;

    private boolean running = true;

    private Queue commandQueue = new Queue();

    private final Thread doCommandThreadTemplate = new Thread() {
        public void run() {
            while (!commandQueue.isEmpty()) {
                doCommand((BlinkState)commandQueue.get());
                synchronized(doCommandThreadTemplate) {
                    if (commandQueue.isEmpty()) {
                        doCommandThread = null;
                        myState.setState(BlinkState.ONLINE_NOTBLINK);
                        return;
                    }
                }
            }
        }
    };

    private Thread doCommandThread = null;

    private Thread listenThread = new Thread() {
        public void run() {
            RadiogramConnection rcvConn=null;
            Radiogram rrg;
            while (running) {
                try {
                    rcvConn = (RadiogramConnection) Connector.open("radiogram://:"+CMD_PORT);
                    rrg = (Radiogram) rcvConn.newDatagram(rcvConn.getMaximumLength());
                    rcvConn.setTimeout(1000);
                    while (running) {
                        try {
                            rrg.reset();
                            rcvConn.receive(rrg);
                            commandQueue.put(new BlinkState(rrg.readByte(), rrg.readByte(), rrg.readByte(), rrg.readByte()));
                            synchronized(doCommandThreadTemplate) {
                                if (doCommandThread == null) {
                                    doCommandThread = new Thread(doCommandThreadTemplate);
                                    doCommandThread.start();
                                }
                            }
                        } catch (TimeoutException ex) {
                        }
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            try {
                doCommandThread.join();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    };

    private void doCommand(BlinkState state) {
        myState = state;
        switch(state.getState()) {
            case BlinkState.ONLINE_BLINK:
                blinkLEDs();
                break;
            default:
                /* command not yet implemented.  ignore */
                System.out.println("Command " + state.getState() + " not implemented");
        }
    }

    private void blinkLEDs() {
        for (int j=0; j<N_BLINK_TIMES; j++) {
            for (int i=0; i<leds.length; i++) {
                leds[i].setColor(myState.getColor());
                leds[i].setOn();
            }
            Utils.sleep(BLINK[0]);
            for (int i=0; i<leds.length; i++) {
                leds[i].setOff();
            }
            Utils.sleep(BLINK[1]-BLINK[0]);
        }
        leds[0].setColor(DEFAULT);
    }

    protected void startApp() throws MIDletStateChangeException {
        System.out.println("Hello, world");
        new BootloaderListener().start();   // monitor the USB (if connected) and recognize commands from host

        long ourAddr = RadioFactory.getRadioPolicyManager().getIEEEAddress();
        System.out.println("Our radio address = " + IEEEAddress.toDottedHex(ourAddr));
        RadioFactory.getRadioPolicyManager().setChannelNumber(CHANNEL);

        ISwitch sw1 = EDemoBoard.getInstance().getSwitches()[EDemoBoard.SW1];
        sw1.addISwitchListener(this);
        leds[0].setRGB(100,0,0);                // set color to moderate red
        listenThread.start();
        RadiogramConnection conn=null;
        Radiogram rg;
        while (running) {
            try {
                conn = (RadiogramConnection) Connector.open("radiogram://broadcast:"+DATA_PORT);
                rg = (Radiogram)conn.newDatagram(conn.getMaximumLength());
                while (running) {                  // done when switch is pressed
                    rg.reset();
                    rg.writeLong(System.currentTimeMillis());
                    rg.writeInt(light.getAverageValue());
                    rg.writeByte(myState.getState());
                    rg.writeByte(myState.getRed());
                    rg.writeByte(myState.getGreen());
                    rg.writeByte(myState.getBlue());
                    conn.send(rg);
                    leds[0].setOn();                    // Blink LED
                    Utils.sleep(PAUSE / 2);                  // wait 1 second
                    leds[0].setOff();
                    Utils.sleep(PAUSE / 2);
                }
            } catch (IOException iOException) {
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

        }
        try {
            listenThread.join();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        notifyDestroyed();                      // cause the MIDlet to exit
    }

    protected void pauseApp() {
        // This is not currently called by the Squawk VM
    }

    /**
     * Called if the MIDlet is terminated by the system.
     * I.e. if startApp throws any exception other than MIDletStateChangeException,
     * if the isolate running the MIDlet is killed with Isolate.exit(), or
     * if VM.stopVM() is called.
     * 
     * It is not called if MIDlet.notifyDestroyed() was called.
     *
     * @param unconditional If true when this method is called, the MIDlet must
     *    cleanup and release all resources. If false the MIDlet may throw
     *    MIDletStateChangeException  to indicate it does not want to be destroyed
     *    at this time.
     */
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
        for (int i = 0; i < 8; i++) {
            leds[i].setOff();
        }
    }

    public void switchPressed(com.sun.spot.resources.transducers.ISwitch sw) {
        running = false;
    }

    public void switchReleased(com.sun.spot.resources.transducers.ISwitch sw) {
    }


}
